package solver

import (
	"fmt"
	"gosudoku/set"
	"log"
	"strings"
)

func verifyHidden(cands []int, candCells []set.Set) (ok bool, cellNums []int) {
	cells := set.NewSet()
	for _, cand := range cands {
		cells = cells.Union(candCells[cand])
	}

	cellNums = cells.GetList()
	ok = len(cellNums) == len(cands)

	return
}

func clearupHiddenHouse(house House, cellNums []int, cands []int) (used bool) {
	for _, cellNum := range cellNums {
		cell := house.cells[cellNum]
		if cell.removeCandsBut(cands) {
			used = true
		}
	}
	return
}

func hiddenHouse(house House, hiddenCount int) (used bool) {
	candCells := sumupHiddenCandCells(house)
	cands := candsForHidden(candCells, hiddenCount)
	subsets := combine(cands, hiddenCount)
	for _, subset := range subsets {
		ok, cellNums := verifyHidden(subset, candCells)
		if !ok {
			continue
		}
		if clearupHiddenHouse(house, cellNums, subset) {
			logNakedHidden("Hidden", house, cellNums, subset)
			used = true
			return
		}
	}

	return
}

func candsForHidden(candCells []set.Set, hiddenCount int) []int {
	cands := []int{}
	for cand, candCell := range candCells {
		if candCell.Count() == 0 || candCell.Count() > hiddenCount {
			continue
		}
		cands = append(cands, cand)
	}
	return cands
}
func sumupHiddenCandCells(house House) (candCells []set.Set) {
	candCells = make([]set.Set, 9)
	for cellNum, cell := range house.cells {
		for _, cand := range cell.cands.GetList() {
			candCells[cand].Add(cellNum)
		}
	}
	return
}
func (g *Grid) HiddenPairHouse(house House) (used bool) {
	return hiddenHouse(house, 2)
}

func (g *Grid) HiddenPair() (used bool) {
	return g.doStrategy(g.HiddenPairHouse)
}

func (g *Grid) HiddenTripleHouse(house House) (used bool) {
	return hiddenHouse(house, 3)
}

func (g *Grid) HiddenTriple() (used bool) {
	return g.doStrategy(g.HiddenTripleHouse)
}

func (g *Grid) HiddenQuandrupleHouse(house House) (used bool) {
	return hiddenHouse(house, 4)
}

func (g *Grid) HiddenQuandruple() (used bool) {
	return g.doStrategy(g.HiddenQuandrupleHouse)
}

// */
func logNakedHidden(nakedOrHidden string, house House, cellNums []int, cands []int) {
	var b strings.Builder

	switch house.kind {
	case 0:
		fmt.Fprintf(&b, "r%vc", house.cells[0].rowNum)
	case 1:
		fmt.Fprintf(&b, "c%vr", house.cells[0].colNum)
	case 2:
		fmt.Fprintf(&b, "b%vn", house.cells[0].boxNum)
	}
	for _, cellNums := range cellNums {
		fmt.Fprintf(&b, "%v", cellNums)
	}
	fmt.Fprint(&b, " ")
	for _, cand := range cands {
		fmt.Fprintf(&b, "%v", toValue(cand))
	}
	switch len(cellNums) {
	case 2:
		fmt.Fprintf(&b, " %v Pair", nakedOrHidden)
	case 3:
		fmt.Fprintf(&b, " %v Triple", nakedOrHidden)
	case 4:
		fmt.Fprintf(&b, " %v Quadruple", nakedOrHidden)
	}
	log.Println(b.String())
}
